package com.vividsolutions.jump.workbench.ui.cursortool;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.feature.AttributeType;
import com.vividsolutions.jump.feature.BasicFeature;
import com.vividsolutions.jump.feature.Feature;
import com.vividsolutions.jump.feature.FeatureDataset;
import com.vividsolutions.jump.feature.FeatureSchema;
import com.vividsolutions.jump.workbench.model.FeatureEventType;
import com.vividsolutions.jump.workbench.model.Layer;
import com.vividsolutions.jump.workbench.model.LayerManager;
import com.vividsolutions.jump.workbench.model.StandardCategoryNames;
import com.vividsolutions.jump.workbench.model.UndoableCommand;
import com.vividsolutions.jump.workbench.ui.EditTransaction;
import com.vividsolutions.jump.workbench.ui.GUIUtil;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.Viewport;
import com.vividsolutions.jump.workbench.ui.images.IconLoader;
import com.vividsolutions.jump.workbench.ui.renderer.style.Style;
import com.vividsolutions.jump.workbench.ui.snap.SnapToFeaturesPolicy;

//Notes layer can now be saved with a task [LDB 12-15-2005]

public class NoteTool extends NClickTool {

    private LayerViewPanel panel;
    
    private abstract class Mode {

        private Feature noteFeature;

        public Mode(Feature noteFeature) {
            this.noteFeature = noteFeature;
        }

        public Coordinate location() {
            return noteFeature.getGeometry().getCoordinate();
        }

        public abstract void commit(String text);

        protected Feature getNoteFeature() {
            return noteFeature;
        }

        public abstract String initialText();
    }

    private class CreateMode extends Mode {

        public CreateMode(final Coordinate location) {
            super(new BasicFeature(layer().getFeatureCollectionWrapper()
                    .getFeatureSchema()) {

                {
                    setAttribute(NoteStyle.CREATED, new Date());
                    setAttribute(NoteStyle.GEOMETRY, new GeometryFactory()
                            .createPoint(location));
                }
            });
        }

        public void commit(String text) {
            if (text.length() > 0) {
                disableAutomaticInitialZooming();
                getNoteFeature().setAttribute(NoteStyle.MODIFIED, new Date());
                getNoteFeature().setAttribute(NoteStyle.TEXT, text);
                EditTransaction transaction = new EditTransaction(
                        Collections.EMPTY_LIST, getName(), layer(),
                        isRollingBackInvalidEdits(), true, getPanel());
                transaction.createFeature(getNoteFeature());
                transaction.commit();
            }
        }

        public String initialText() {
            return "";
        }
    }

    private class EditMode extends Mode {

        public EditMode(Feature noteFeature) {
            super(noteFeature);
        }

        public void commit(final String text) {
            final Date modifiedDate = new Date();
            final Date oldModifiedDate = (Date) getNoteFeature().getAttribute(
            		NoteStyle.MODIFIED);
            final String oldText = getNoteFeature().getString(NoteStyle.TEXT);
            execute(new UndoableCommand(getName()) {

                public void execute() {
                    update(getNoteFeature(), text, modifiedDate, layer());
                }

                public void unexecute() {
                    update(getNoteFeature(), oldText, oldModifiedDate, layer());
                }
            });
        }

        private void update(Feature noteFeature, String text,
                Date modifiedDate, Layer layer) {
            noteFeature.setAttribute(NoteStyle.MODIFIED, modifiedDate);
            noteFeature.setAttribute(NoteStyle.TEXT, text);
            layer.getLayerManager().fireFeaturesChanged(
                    Collections.singleton(noteFeature),
                    FeatureEventType.ATTRIBUTES_MODIFIED, layer);
        }

        public String initialText() {
            return getNoteFeature().getString(NoteStyle.TEXT);
        }
    }

    public NoteTool() {
        super(1);
        panel = getPanel();
        getSnapManager().addPolicies(
                Collections.singleton(new SnapToFeaturesPolicy()));
        textArea = NoteStyle.createTextArea();
        textArea.addFocusListener(new FocusAdapter() {

            public void focusLost(FocusEvent e) {
                if (panelContainsTextArea())
                {
                	boolean doit = (textArea.getText().trim().length() > 0);
                	if (doit) getPanel().getLayerManager().getUndoableEditReceiver().startReceiving();
                	removeTextAreaFromPanel();
                	if (doit) getPanel().getLayerManager().getUndoableEditReceiver().stopReceiving();        }
            	}
        });
    }

    private JTextArea textArea;

    private Mode mode;

    public void deactivate() 
    {
        if (panelContainsTextArea())
        {
        	boolean doit = (textArea.getText().trim().length() > 0);
        	if (doit) getPanel().getLayerManager().getUndoableEditReceiver().startReceiving();
        	removeTextAreaFromPanel();
        	if (doit) getPanel().getLayerManager().getUndoableEditReceiver().stopReceiving();        
        	}
    	super.deactivate();
    }

    public Cursor getCursor() {
        return cursor;
    }

    protected void gestureFinished() throws Exception {
        reportNothingToUndoYet();
        Feature noteFeatureAtClick = noteFeature(getModelDestination());
        removeTextAreaFromPanel();
        mode = mode(noteFeatureAtClick, getModelDestination());

        //Since focusLost will be called after gestureFinished() is called
        //any testArea added here would be removed.
        //However the invokeLater will be called after any other calls to removeTextAreaFromPanel()
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    addTextAreaToPanel(mode.location());
                } catch (NoninvertibleTransformException e) {
                    getPanel().getContext().handleThrowable(e);
                }
            }
        });
    }

    private Mode mode(Feature noteFeatureAtClick, Coordinate click) {
        return noteFeatureAtClick == null ? (Mode) new CreateMode(click)
                : new EditMode(noteFeatureAtClick);
    }

    private Feature noteFeature(Coordinate click) {
        return noteFeature(new Envelope(click, new Coordinate(click.x - NoteStyle.WIDTH
                / scale(), click.y + NoteStyle.HEIGHT / scale())));
    }

    private Feature noteFeature(Envelope envelope) {
        return (Feature) firstOrNull(layer().getFeatureCollectionWrapper()
                .query(envelope));
    }

    private Object firstOrNull(Collection items) {
        return !items.isEmpty() ? items.iterator().next() : null;
    }

    private double scale() {
        return getPanel().getViewport().getScale();
    }

    private void addTextAreaToPanel(Coordinate location)
            throws NoninvertibleTransformException {
        layer().setVisible(true);
        if (getPanel().getLayout() != null) {
            getPanel().setLayout(null);
        }
        textArea.setText(mode.initialText());
        textArea.setBackground(layer().getBasicStyle().getFillColor());
        getPanel().add(textArea);
        
        textArea.addCaretListener(new CaretListener()
        {
        	public void caretUpdate(CaretEvent e)
        	{
        		JTextArea textArea = (JTextArea)e.getSource();
        		int ht = textArea.getPreferredSize().height;
        		int wt = textArea.getPreferredSize().width;
        		if (ht < NoteStyle.HEIGHT) ht = NoteStyle.HEIGHT;
        		if (wt < NoteStyle.WIDTH) wt = NoteStyle.WIDTH;
        		int x = textArea.getBounds().x;
        		int y = textArea.getBounds().y;
                textArea.setBounds(x, y, wt, ht);
        	}
        });
        
		int ht = textArea.getPreferredSize().height;
		int wt = textArea.getPreferredSize().width;
		if (ht < NoteStyle.HEIGHT) ht = NoteStyle.HEIGHT;
		if (wt < NoteStyle.WIDTH) wt = NoteStyle.WIDTH;
        textArea.setBounds((int) getPanel().getViewport().toViewPoint(location)
                .getX(), (int) getPanel().getViewport().toViewPoint(location)
                .getY(), wt, ht);

        //textArea.setBounds((int) getPanel().getViewport().toViewPoint(location)
        //        .getX(), (int) getPanel().getViewport().toViewPoint(location)
        //        .getY(), NoteStyle.WIDTH, NoteStyle.HEIGHT);
        textArea.requestFocus();
    }

    private boolean panelContainsTextArea() {
        return Arrays.asList(getPanel().getComponents()).contains(textArea);
    }

    private void removeTextAreaFromPanel() {
        if (!panelContainsTextArea()) {return;}
        mode.commit(textArea.getText().trim());
        getPanel().remove(textArea);
        getPanel().superRepaint();
    }

    private void disableAutomaticInitialZooming() {
        getPanel().setViewportInitialized(true);
    }

     public Layer layer() {
     	LayerManager layerManager = getPanel().getLayerManager();
    	if (layerManager.getLayer(NoteStyle.NAME) != null) {
    		return layerManager.getLayer(NoteStyle.NAME);
    	}
    	Layer noteLayer = new Layer(NoteStyle.NAME, Color.yellow.brighter().brighter(),
    		new FeatureDataset(NoteStyle.createFeatureSchema()), layerManager);
    	
		boolean firingEvents = layerManager.isFiringEvents();
		layerManager.setFiringEvents(false);
		try {
	    	noteLayer.addStyle(new NoteStyle());
		} finally {
			layerManager.setFiringEvents(firingEvents);
		}    	
		layerManager.addLayer(StandardCategoryNames.SYSTEM, noteLayer);
    	return noteLayer;
    }

    public Icon getIcon() {
        return icon;
    }

 
    protected Shape getShape() throws NoninvertibleTransformException {
        return null;
    }

    private ImageIcon icon = IconLoader.icon("sticky.png");

    private Cursor cursor = GUIUtil.createCursorFromIcon(icon.getImage());

}
